package ice.server.comm;

import org.apache.commons.lang3.StringUtils;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSourceUtils;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
import javax.persistence.Table;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.Collection;

/**
 * 基于JdbcTemplate的批量操作实现类
 *
 * @author : 张美景
 * @date : 2019.05.15 11:51
 */
public abstract class BaseBatchServiceImpl<T> implements BaseBatchService<T> {

    @Resource
    private NamedParameterJdbcTemplate namedParameterJdbcTemplate;

    private  Class<T> entityClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];

    /**
     * 批量保存
     *
     * @author : 张美景
     * @date : 2019.05.15 11:51
     * @param beanClass 对象类型
     * @param beans 要保存的数据
     * @return 保存成功的信息id
     */
    private int[] batchSave(Class<T> beanClass, Collection<T> beans) {

        return this.batchSave(beanClass, beans, null);
    }

    /**
     * 批量保存
     *
     * @author : 张美景
     * @date : 2019.05.15 11:51
     * @param beanClass 对象类型
     * @param beans 要保存的数据
     * @param tableName 数据库表名
     * @return 保存成功的信息id
     */
    private int[] batchSave(Class<T> beanClass, Collection<T> beans, String tableName) {

        if(CollectionUtils.isEmpty(beans)){
            return new int[]{};
        }

        SqlParameterSource[] beanSources = SqlParameterSourceUtils.createBatch(beans.toArray());

        return namedParameterJdbcTemplate.batchUpdate(this.getInsertSql(beanClass, tableName), beanSources);

    }

    /**
     * 保存SQL语句
     *
     * @author : 张美景
     * @date : 2019.05.15 11:51
     * @return SQL语句
     */
    private String getInsertSql(Class<T> beanClass, String tableName){

        StringBuilder sql = new StringBuilder("insert into ");

        StringBuilder valueSql = new StringBuilder("values(:");

        if(StringUtils.isBlank(tableName)){
            Table table = beanClass.getAnnotation(Table.class);
            if(table == null){
                throw new RuntimeException("没有获取到该类上的注解@Table");
            }
            sql.append(table.name()).append(" (");
        } else {
            sql.append(tableName).append(" (");
        }

        try {
            //使用内省机制获取BeanInfo对象
            BeanInfo info = Introspector.getBeanInfo(beanClass, Object.class);
            //获取beanClass中所有的属性
            PropertyDescriptor[] descriptors = info.getPropertyDescriptors();
            for (PropertyDescriptor descriptor : descriptors) {

                if("id".equals(descriptor.getName())){
                    continue;
                }

                Column column = descriptor.getReadMethod().getAnnotation(Column.class);
                if(column == null){
                    //获取指定字段的Column注解
                    column = this.getDeclaredField(beanClass, descriptor.getName());

                }

                if(column == null){
                   continue;
                }

                sql.append(column.name()).append(", ");
                valueSql.append(descriptor.getName()).append(", :");
            }

            if(sql.toString().endsWith(", ")){
                sql = new StringBuilder(sql.substring(0, sql.length() - 2));

                valueSql = new StringBuilder(valueSql.substring(0, valueSql.length() - 3));
            }

            sql.append(") ").append(valueSql.append(")"));

        } catch (IntrospectionException e) {
            throw new RuntimeException("内省机制获取BeanInfo对象时发生异常", e);
        }

        return sql.toString();
    }

    /**
     * 获取指定字段的Column注解
     * @param beanClass 类的Class类型
     * @param fieldName 字段名
     * @return Column注解信息
     */
    private Column getDeclaredField(Class<?> beanClass, String fieldName) {

        if(beanClass.isAssignableFrom(Object.class)){
            return null;
        }

        Field[] declaredFields = beanClass.getDeclaredFields();
        if(declaredFields == null || declaredFields.length == 0){
            return null;
        }

        for (Field field : declaredFields) {
            if(!field.getName().equals(fieldName)){
               continue;
            }

            try {
                return beanClass.getDeclaredField(fieldName).getAnnotation(Column.class);
            } catch (NoSuchFieldException e) {
                throw new RuntimeException("反射机制获取字段信息时发生异常", e);
            }
        }

        Class<?> superClass = beanClass.getSuperclass();
        MappedSuperclass mappedSuperclass = superClass.getAnnotation(MappedSuperclass.class);

        return mappedSuperclass == null ? null : this.getDeclaredField(superClass, fieldName);
    }

    /**
     * 批量修改
     *
     * @author : 张美景
     * @date : 2019.05.15 11:51
     * @param beanClass 对象类型
     * @param beans 要修改的数据
     * @return 修改成功的信息id
     */
    private int[] batchUpdate(Class<T> beanClass, Collection<T> beans) {

        if(CollectionUtils.isEmpty(beans)){
            return null;
        }

        SqlParameterSource[] beanSources = SqlParameterSourceUtils.createBatch(beans.toArray());

        StringBuilder sql = new StringBuilder("update ");

        Table table = beanClass.getAnnotation(Table.class);
        if(table == null){
            throw new RuntimeException("没有获取到该类上的注解@Table");
        }
        sql.append(table.name()).append(" set ");

        try {
            //使用内省机制获取BeanInfo对象
            BeanInfo info = Introspector.getBeanInfo(beanClass, Object.class);

            //获取beanClass中所有的属性
            PropertyDescriptor[] descriptors = info.getPropertyDescriptors();

            for (PropertyDescriptor descriptor : descriptors) {

                if("id".equals(descriptor.getName())){
                    continue;
                }

                Column column = descriptor.getReadMethod().getAnnotation(Column.class);
                if(column == null){
                    //获取指定字段的Column注解
                    column = this.getDeclaredField(beanClass, descriptor.getName());

                }

                if(column == null){
                    continue;
                }

                sql.append(column.name()).append(" = :");
                sql.append(descriptor.getName()).append(", ");
            }

            if(sql.toString().endsWith(", ")){
                sql = new StringBuilder(sql.substring(0, sql.length() - 2));
            }

            sql.append(" where id = :id");

        } catch (IntrospectionException e) {
            e.printStackTrace();
            throw new RuntimeException("内省机制获取BeanInfo对象时发生异常", e);
        }

        return namedParameterJdbcTemplate.batchUpdate(sql.toString(), beanSources);
    }

    @Override
    public int[] batchSave(Collection<T> beans) {
        return this.batchSave(entityClass, beans);
    }

    @Override
    public int[] batchSave(Collection<T> beans, String tableName) {
        return this.batchSave(entityClass, beans, tableName);
    }

    @Override
    public int[] batchUpdate(Collection<T> beans) {
        return this.batchUpdate(entityClass, beans);
    }


}
